<!DOCTYPE html>
<html lang="zh-cn">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>成三棋</title>
    <style>
        * {
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }

        html,
        body {
            width: 100vw;
            height: 100vh;
            background-color: #1a1a1a;
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            overflow: hidden;
            color: white;
            font-family: Arial, sans-serif;
        }

        .game-container {
            position: relative;
            width: 90vw;
            height: 90vw;
            max-width: 600px;
            max-height: 600px;
        }

        #canvas {
            width: 100%;
            height: 100%;
            border-radius: 8px;
        }

        #message {
            position: fixed;
            top: 20px;
            padding: 8px 16px;
            font-size: 16px;
            border-radius: 6px;
            background-color: rgba(0, 0, 0, 0.7);
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.5s ease;
            z-index: 10;
        }

        .controls {
            position: fixed;
            bottom: 20px;
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            justify-content: center;
            align-items: center;
            padding: 10px;
            background-color: rgba(0, 0, 0, 0.5);
            border-radius: 8px;
        }

        button {
            padding: 8px 16px;
            border: none;
            border-radius: 4px;
            background-color: #4CAF50;
            color: white;
            cursor: pointer;
            transition: background-color 0.3s;
            font-size: 14px;
        }

        button:hover {
            background-color: #45a049;
        }

        .background-selector {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .background-options {
            display: flex;
            gap: 6px;
        }

        .bg-option {
            width: 24px;
            height: 24px;
            border-radius: 4px;
            cursor: pointer;
            border: 2px solid transparent;
            transition: transform 0.2s;
        }

        .bg-option:hover {
            transform: scale(1.1);
        }

        .bg-option.selected {
            border-color: #4CAF50;
        }
    </style>
</head>

<body>
    <div id="message"></div>
    <div class="game-container">
        <canvas id="canvas"></canvas>
    </div>
    <div class="controls">
        <div class="background-selector">
            <span>棋盘背景:</span>
            <div class="background-options">
                <div class="bg-option selected" style="background-color: #8b4513;" data-bg="wood"></div>
                <div class="bg-option" style="background-color: #2f4f4f;" data-bg="stone"></div>
                <div class="bg-option" style="background-color: #000000;" data-bg="black"></div>
                <div class="bg-option" style="background-color: #2e8b57;" data-bg="green"></div>
            </div>
        </div>
        <button id="restart">重新开始</button>
        <button id="deselect">取消选择</button>
    </div>
    <script>
        // 游戏配置
        const gameConfig = {
            baseBoardSize: 600,    // 基准棋盘尺寸
            marginRatio: 0.05,     // 边距占棋盘比例
            pieceRadiusRatio: 0.025, // 棋子半径占棋盘比例
            maxPieces: 18,         // 最大棋子数（2*9）
            validMoveMarkerSize: 0.5, // 可落子标记大小（相对于棋子半径）
            validMoveMarkerOpacity: 0.6, // 可落子标记透明度
            adjacencyTolerance: 1.5, // 点位相邻判定容差（像素）
            boardBackgrounds: {
                wood: {
                    color: '#8b4513',
                    lineColor: '#deb887',
                    lineColorSecondary: '#cd853f'
                },
                stone: {
                    color: '#2f4f4f',
                    lineColor: '#d3d3d3',
                    lineColorSecondary: '#a9a9a9'
                },
                black: {
                    color: '#000000',
                    lineColor: '#ffffff',
                    lineColorSecondary: '#dddddd'
                },
                green: {
                    color: '#2e8b57',
                    lineColor: '#98fb98',
                    lineColorSecondary: '#3cb371'
                }
            },
            currentBackground: 'wood'
        };

        // 游戏状态枚举
        const GameStage = {
            PLACING: 'placing',    // 落子阶段
            MOVING: 'moving'       // 行棋阶段
        };
        const Player = {
            WHITE: { 
                color: '#ffffff',     // 白棋基础色
                darkShade: '#dddddd', // 白棋暗色调
                lightShade: '#ffffff' // 白棋亮色调
            },
            RED: { 
                color: '#ff4400',     // 红棋基础色
                darkShade: '#cc3300', // 红棋暗色调
                lightShade: '#ff8855' // 红棋亮色调
            }
        };

        // 全局状态
        let canvas, ctx;
        let boardSize;       // 自适应后棋盘尺寸
        let margin;          // 自适应后边距
        let gap;             // 自适应后格子间隔
        let pieceRadius;     // 自适应后棋子半径
        let points = [];     // 棋盘交点（绝对坐标，带唯一标识）
        let pieces = [];     // 已落棋子
        let adjacencyMap = {}; // 邻接表（键为点位ID，值为相邻点位列表）
        let currentStage = GameStage.PLACING;
        let currentPlayer = Player.WHITE;
        let selectedPiece = null; // 选中待移动的棋子
        let lastEatenPositionId = null; // 记录最后被吃棋子的点位ID（用于冷却）
        let eatenPositionCooldown = false; // 被吃位置的冷却状态


        // 工具函数
        const generateId = () => Math.random().toString(36).slice(2, 15);
        const isSamePoint = (point1, point2) => point1.id === point2.id; // 用ID判断点位一致性
        const showTip = (content) => {
            const tipEl = document.getElementById('message');
            tipEl.textContent = content;
            tipEl.style.opacity = 1;
            setTimeout(() => tipEl.style.opacity = 0, 2000);
        };
        const getBackgroundConfig = () => gameConfig.boardBackgrounds[gameConfig.currentBackground];


        // 棋子类
        class Piece {
            constructor(point, player) {
                this.point = point; // 关联的棋盘点位（含ID、x、y）
                this.x = point.x;   // 缓存x坐标
                this.y = point.y;   // 缓存y坐标
                this.player = player;
                this.isSelected = false;
                this.isEatable = false;
                this.isInThree = false; // 标记是否在三子连线中
                this.id = generateId();
                this.gradients = this.createGradients();
            }

            createGradients() {
                const radius = pieceRadius;
                
                // 主体颜色渐变 - 确保玩家主色占主导
                const mainGrad = ctx.createRadialGradient(
                    this.x - radius * 0.3, this.y - radius * 0.3, 0,
                    this.x, this.y, radius
                );
                // 调整渐变比例，让主色占据更大面积
                mainGrad.addColorStop(0, this.player.lightShade);    // 高光（占比小）
                mainGrad.addColorStop(0.3, this.player.color);       // 主色（占比大）
                mainGrad.addColorStop(1, this.player.darkShade);     // 阴影（占比适中）

                // 边缘阴影渐变 - 增强立体感但不影响主色
                const shadowGrad = ctx.createLinearGradient(
                    this.x - radius * 0.5, this.y - radius * 0.5,
                    this.x + radius * 0.5, this.y + radius * 0.5
                );
                shadowGrad.addColorStop(0, 'rgba(0,0,0,0.3)');
                shadowGrad.addColorStop(1, 'rgba(0,0,0,0)');

                return { mainGrad, shadowGrad };
            }

            draw() {
                ctx.save();
                const radius = pieceRadius;

                // 绘制底层阴影（不影响主色）
                ctx.shadowColor = 'rgba(0,0,0,0.5)';
                ctx.shadowOffsetX = 3;
                ctx.shadowOffsetY = 3;
                ctx.shadowBlur = 5;

                // 绘制棋子主体
                ctx.beginPath();
                ctx.arc(this.x, this.y, radius, 0, Math.PI * 2);
                
                // 填充主色渐变（确保主色清晰）
                ctx.fillStyle = this.gradients.mainGrad;
                ctx.fill();
                
                // 绘制边缘阴影（增强立体感）
                ctx.globalCompositeOperation = 'multiply';
                ctx.fillStyle = this.gradients.shadowGrad;
                ctx.fill();

                // 恢复默认混合模式
                ctx.globalCompositeOperation = 'source-over';
                ctx.shadowColor = 'rgba(0,0,0,0)';
                ctx.closePath();

                // 状态标记样式
                if (this.isSelected) {
                    // 选中状态：品红色实线边框
                    ctx.strokeStyle = 'rgba(255,0,255,0.9)';
                    ctx.lineWidth = 2.5;
                    ctx.stroke();
                } else if (this.isEatable) {
                    // 可吃状态：红色虚线边框
                    ctx.strokeStyle = 'rgba(255,0,0,0.8)';
                    ctx.lineWidth = 2;
                    ctx.setLineDash([4, 2]);
                    ctx.stroke();
                    ctx.setLineDash([]);
                }

                // 三子连线标记
                if (this.isInThree) {
                    ctx.strokeStyle = 'rgba(0,255,0,0.6)';
                    ctx.lineWidth = 1.5;
                    ctx.setLineDash([3, 3]);
                    ctx.stroke();
                    ctx.setLineDash([]);
                }

                // 选中时绘制可落子位置
                if (this.isSelected) {
                    this.drawValidMoves();
                }

                ctx.restore();
            }

            // 绘制可落子位置
            drawValidMoves() {
                // 1. 从邻接表获取当前棋子点位的所有相邻点位
                const adjacentPoints = adjacencyMap[this.point.id] || [];
                // 2. 过滤：仅保留「无棋子占用」的点位
                // 行棋阶段忽略冷却限制，落棋阶段考虑冷却限制
                const validMovePoints = adjacentPoints.filter(point => {
                    // 检查点位是否被占用
                    const isOccupied = pieces.some(p => isSamePoint(p.point, point));
                    // 检查点位是否是冷却中的被吃位置（仅落棋阶段生效）
                    const isCooling = currentStage === GameStage.PLACING && 
                                     lastEatenPositionId === point.id && 
                                     eatenPositionCooldown;
                    return !isOccupied && !isCooling;
                });

                // 3. 绘制可落子标记
                validMovePoints.forEach(point => {
                    ctx.save();
                    ctx.beginPath();
                    const markerRadius = pieceRadius * gameConfig.validMoveMarkerSize;
                    // 标记阴影
                    ctx.shadowColor = 'rgba(255,255,255,0.5)';
                    ctx.shadowBlur = 4;
                    // 标记主体
                    ctx.arc(point.x, point.y, markerRadius, 0, Math.PI * 2);
                    ctx.fillStyle = `rgba(255,255,255,${gameConfig.validMoveMarkerOpacity})`;
                    ctx.fill();
                    ctx.closePath();
                    ctx.restore();
                });
            }
        }


        // 棋盘绘制与自适应
        function calcAdaptiveParams() {
            const canvasEl = document.getElementById('canvas');
            const displaySize = Math.min(canvasEl.clientWidth, canvasEl.clientHeight);
            
            // 高DPI屏幕适配
            const dpr = window.devicePixelRatio || 1;
            canvasEl.width = displaySize * dpr;
            canvasEl.height = displaySize * dpr;
            canvasEl.style.width = `${displaySize}px`;
            canvasEl.style.height = `${displaySize}px`;
            
            // 缩放上下文
            ctx.scale(dpr, dpr);
            
            // 计算棋盘核心参数
            boardSize = displaySize * (1 - 2 * gameConfig.marginRatio);
            margin = displaySize * gameConfig.marginRatio;
            gap = boardSize / 6; // 棋盘网格间隔
            pieceRadius = boardSize * gameConfig.pieceRadiusRatio;
        }

        // 生成棋盘所有连线
        function getBoardLines() {
            return {
                // 主横线
                mainHLines: [
                    [0, 0, boardSize, 0],                  // 上边缘
                    [boardSize/3, boardSize/3, 2*boardSize/3, boardSize/3], // 上三分之一线
                    [boardSize/3, 2*boardSize/3, 2*boardSize/3, 2*boardSize/3], // 下三分之一线
                    [0, boardSize, boardSize, boardSize]   // 下边缘
                ],
                // 主竖线
                mainVLines: [
                    [0, 0, 0, boardSize],                  // 左边缘
                    [boardSize/3, boardSize/3, boardSize/3, 2*boardSize/3], // 左三分之一线
                    [2*boardSize/3, boardSize/3, 2*boardSize/3, 2*boardSize/3], // 右三分之一线
                    [boardSize, 0, boardSize, boardSize]   // 右边缘
                ],
                // 辅助横线
                auxHLines: [
                    [gap, gap, boardSize - gap, gap],      // 上辅助线
                    [gap, 5*gap, boardSize - gap, 5*gap]   // 下辅助线
                ],
                // 辅助竖线
                auxVLines: [
                    [gap, gap, gap, boardSize - gap],      // 左辅助线
                    [5*gap, gap, 5*gap, boardSize - gap]   // 右辅助线
                ],
                // 中线
                midLines: [
                    [boardSize/2, 0, boardSize/2, 2*gap],  // 上中线
                    [boardSize/2, boardSize - 2*gap, boardSize/2, boardSize], // 下中线
                    [0, boardSize/2, 2*gap, boardSize/2],  // 左中线
                    [boardSize - 2*gap, boardSize/2, boardSize, boardSize/2]  // 右中线
                ]
            };
        }

        // 生成棋盘所有点位（带唯一ID）- 修复了第二列中间一行点位缺失问题
        function generateBoardPoints() {
            points = [];
            
            // 生成带ID的点位
            const createPoint = (x, y) => {
                const point = { x, y, id: generateId() };
                points.push(point);
                return point;
            };

            // 1. 边缘点位
            createPoint(0 + margin, 0 + margin);          // 左上角
            createPoint(boardSize/2 + margin, 0 + margin); // 上中点
            createPoint(boardSize + margin, 0 + margin);   // 右上角
            createPoint(0 + margin, boardSize + margin);   // 左下角
            createPoint(boardSize/2 + margin, boardSize + margin); // 下中点
            createPoint(boardSize + margin, boardSize + margin);   // 右下角
            createPoint(0 + margin, boardSize/2 + margin); // 左中点
            createPoint(boardSize + margin, boardSize/2 + margin);  // 右中点

            // 2. 辅助线点位
            createPoint(gap + margin, gap + margin);          // 上辅助线左
            createPoint(boardSize/2 + margin, gap + margin);   // 上辅助线中
            createPoint((boardSize - gap) + margin, gap + margin); // 上辅助线右
            createPoint(gap + margin, 5*gap + margin);        // 下辅助线左
            createPoint(boardSize/2 + margin, 5*gap + margin); // 下辅助线中
            createPoint((boardSize - gap) + margin, 5*gap + margin); // 下辅助线右

            // 3. 中间区域点位
            createPoint(boardSize/3 + margin, boardSize/3 + margin); // 上左
            createPoint(boardSize/2 + margin, boardSize/3 + margin); // 上中
            createPoint(2*boardSize/3 + margin, boardSize/3 + margin); // 上右
            
            // 修复：添加第二列中间一行的关键点位
            createPoint(boardSize/3 + margin, boardSize/2 + margin); // 中左（第二列中间行）
            createPoint(boardSize/2 + margin, boardSize/2 + margin); // 中心
            createPoint(2*boardSize/3 + margin, boardSize/2 + margin); // 中右
            
            createPoint(boardSize/3 + margin, 2*boardSize/3 + margin); // 下左
            createPoint(boardSize/2 + margin, 2*boardSize/3 + margin); // 下中
            createPoint(2*boardSize/3 + margin, 2*boardSize/3 + margin); // 下右
        }

        // 构建邻接表
        function buildAdjacencyMap() {
            adjacencyMap = {};
            const { mainHLines, mainVLines, auxHLines, auxVLines, midLines } = getBoardLines();
            const allLines = [...mainHLines, ...mainVLines, ...auxHLines, ...auxVLines, ...midLines];
            const tolerance = gameConfig.adjacencyTolerance;

            // 初始化每个点位的邻接列表
            points.forEach(point => {
                adjacencyMap[point.id] = [];
            });

            // 处理每条线，建立相邻关系
            allLines.forEach(([x1, y1, x2, y2]) => {
                // 找出在线上的所有点位
                const linePoints = points.filter(point => {
                    const relX = point.x - margin;
                    const relY = point.y - margin;
                    
                    // 水平线 (y坐标相同)
                    if (y1 === y2 && Math.abs(relY - y1) < tolerance) {
                        return relX >= Math.min(x1, x2) - tolerance && relX <= Math.max(x1, x2) + tolerance;
                    }
                    
                    // 垂直线 (x坐标相同)
                    if (x1 === x2 && Math.abs(relX - x1) < tolerance) {
                        return relY >= Math.min(y1, y2) - tolerance && relY <= Math.max(y1, y2) + tolerance;
                    }
                    
                    return false;
                });

                // 按位置排序并建立相邻关系
                if (y1 === y2) {
                    // 水平线：按x坐标排序
                    linePoints.sort((a, b) => a.x - b.x);
                } else {
                    // 垂直线：按y坐标排序
                    linePoints.sort((a, b) => a.y - b.y);
                }

                // 为相邻点位建立关联
                for (let i = 0; i < linePoints.length - 1; i++) {
                    const p1 = linePoints[i];
                    const p2 = linePoints[i + 1];
                    
                    // 计算两点距离，确保是直接相邻而非跨点
                    const distance = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
                    const isDirectlyAdjacent = distance < (gap * 1.1); // 允许10%误差
                    
                    if (isDirectlyAdjacent) {
                        // 避免重复添加
                        if (!adjacencyMap[p1.id].some(p => isSamePoint(p, p2))) {
                            adjacencyMap[p1.id].push(p2);
                        }
                        if (!adjacencyMap[p2.id].some(p => isSamePoint(p, p1))) {
                            adjacencyMap[p2.id].push(p1);
                        }
                    }
                }
            });
        }

        function drawBoard() {
            const bg = getBackgroundConfig();
            const { mainHLines, mainVLines, auxHLines, auxVLines, midLines } = getBoardLines();
            
            // 清空画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            
            // 绘制棋盘背景
            ctx.fillStyle = bg.color;
            ctx.fillRect(margin/2, margin/2, boardSize + margin, boardSize + margin);
            
            // 添加背景纹理
            ctx.save();
            ctx.fillStyle = 'rgba(255, 255, 255, 0.03)';
            for (let i = 0; i < 50; i++) {
                const x = margin + Math.random() * boardSize;
                const y = margin + Math.random() * boardSize;
                const size = Math.random() * 5 + 1;
                ctx.beginPath();
                ctx.arc(x, y, size, 0, Math.PI * 2);
                ctx.fill();
            }
            ctx.restore();

            // 绘制线条（区分主辅线样式）
            const drawLines = (lines, style = { width: 2, color: '#fff' }) => {
                lines.forEach(([x1, y1, x2, y2]) => {
                    ctx.beginPath();
                    ctx.moveTo(x1 + margin, y1 + margin);
                    ctx.lineTo(x2 + margin, y2 + margin);
                    ctx.strokeStyle = style.color;
                    ctx.lineWidth = style.width;
                    ctx.stroke();
                });
            };

            // 主线条（粗一点）
            drawLines([...mainHLines, ...mainVLines], { width: 2.5, color: bg.lineColor });
            // 辅助线条（细一点，颜色稍浅）
            drawLines([...auxHLines, ...auxVLines, ...midLines], { width: 1.8, color: bg.lineColorSecondary });
            
            // 绘制被吃位置的冷却标记（仅在落棋阶段显示）
            if (currentStage === GameStage.PLACING && lastEatenPositionId && eatenPositionCooldown) {
                const cooledPoint = points.find(p => p.id === lastEatenPositionId);
                if (cooledPoint) {
                    ctx.beginPath();
                    ctx.arc(cooledPoint.x, cooledPoint.y, pieceRadius * 1.2, 0, Math.PI * 2);
                    ctx.strokeStyle = 'rgba(255,255,0,0.5)';
                    ctx.lineWidth = 2;
                    ctx.setLineDash([4, 2]);
                    ctx.stroke();
                    ctx.setLineDash([]);
                }
            }
            
            generateBoardPoints();
            buildAdjacencyMap();
        }


        // 游戏逻辑：检查是否形成三子连线
        function checkThreeInARow(piece) {
            // 重置所有棋子的三子连线标记
            pieces.forEach(p => p.isInThree = false);
            
            const sameColorPieces = pieces.filter(p => p.player === piece.player);
            const targetPoint = piece.point;

            // 检查水平和垂直方向
            const checkDirection = (isHorizontal) => {
                let count = 1;
                let currentPoint = targetPoint;
                let linePieces = [piece];
                
                // 正方向查询
                while (true) {
                    const nextPoints = adjacencyMap[currentPoint.id] || [];
                    const nextPoint = nextPoints.find(p => {
                        return isHorizontal 
                            ? Math.abs(p.y - currentPoint.y) < gameConfig.adjacencyTolerance && p.x > currentPoint.x 
                            : Math.abs(p.x - currentPoint.x) < gameConfig.adjacencyTolerance && p.y > currentPoint.y;
                    });

                    if (!nextPoint) break;
                    
                    const nextPiece = sameColorPieces.find(p => isSamePoint(p.point, nextPoint));
                    if (nextPiece) {
                        count++;
                        currentPoint = nextPoint;
                        linePieces.push(nextPiece);
                    } else {
                        break;
                    }
                }

                // 负方向查询
                currentPoint = targetPoint;
                while (true) {
                    const prevPoints = adjacencyMap[currentPoint.id] || [];
                    const prevPoint = prevPoints.find(p => {
                        return isHorizontal 
                            ? Math.abs(p.y - currentPoint.y) < gameConfig.adjacencyTolerance && p.x < currentPoint.x 
                            : Math.abs(p.x - currentPoint.x) < gameConfig.adjacencyTolerance && p.y < currentPoint.y;
                    });

                    if (!prevPoint) break;
                    
                    const prevPiece = sameColorPieces.find(p => isSamePoint(p.point, prevPoint));
                    if (prevPiece) {
                        count++;
                        currentPoint = prevPoint;
                        linePieces.push(prevPiece);
                    } else {
                        break;
                    }
                }

                // 标记三子连线中的棋子
                if (count >= 3) {
                    linePieces.forEach(p => p.isInThree = true);
                    return true;
                }
                return false;
            };

            return checkDirection(true) || checkDirection(false);
        }

        // 获取对方所有的三子连线
        function getOpponentThreeLines(opponentPlayer) {
            const opponentPieces = pieces.filter(p => p.player === opponentPlayer);
            const threeLines = new Set();
            
            opponentPieces.forEach(piece => {
                const sameColorPieces = pieces.filter(p => p.player === opponentPlayer);
                const targetPoint = piece.point;
                
                const checkDirection = (isHorizontal) => {
                    let count = 1;
                    let currentPoint = targetPoint;
                    let linePieces = [piece];
                    
                    // 正方向查询
                    while (true) {
                        const nextPoints = adjacencyMap[currentPoint.id] || [];
                        const nextPoint = nextPoints.find(p => {
                            return isHorizontal 
                                ? Math.abs(p.y - currentPoint.y) < gameConfig.adjacencyTolerance && p.x > currentPoint.x 
                                : Math.abs(p.x - currentPoint.x) < gameConfig.adjacencyTolerance && p.y > currentPoint.y;
                        });

                        if (!nextPoint) break;
                        
                        const nextPiece = sameColorPieces.find(p => isSamePoint(p.point, nextPoint));
                        if (nextPiece) {
                            count++;
                            currentPoint = nextPoint;
                            linePieces.push(nextPiece);
                        } else {
                            break;
                        }
                    }

                    // 负方向查询
                    currentPoint = targetPoint;
                    while (true) {
                        const prevPoints = adjacencyMap[currentPoint.id] || [];
                        const prevPoint = prevPoints.find(p => {
                            return isHorizontal 
                                ? Math.abs(p.y - currentPoint.y) < gameConfig.adjacencyTolerance && p.x < currentPoint.x 
                                : Math.abs(p.x - currentPoint.x) < gameConfig.adjacencyTolerance && p.y < currentPoint.y;
                        });

                        if (!prevPoint) break;
                        
                        const prevPiece = sameColorPieces.find(p => isSamePoint(p.point, prevPoint));
                        if (prevPiece) {
                            count++;
                            currentPoint = prevPoint;
                            linePieces.push(prevPiece);
                        } else {
                            break;
                        }
                    }

                    // 记录三子连线中的棋子ID
                    if (count >= 3) {
                        linePieces.forEach(p => threeLines.add(p.id));
                    }
                };

                checkDirection(true);
                checkDirection(false);
            });
            
            return threeLines;
        }

        // 检查是否胜利（仅在行棋阶段）
        function checkWin() {
            // 只有在行棋阶段才进行最终胜利判定
            if (currentStage !== GameStage.MOVING) {
                return false;
            }
            
            // 胜利条件：对方棋子少于3个
            const opponentPieces = pieces.filter(p => p.player !== currentPlayer);
            return opponentPieces.length < 3;
        }

        function markEatablePieces(winner) {
            // 获取对方玩家
            const opponentPlayer = winner === Player.WHITE ? Player.RED : Player.WHITE;
            // 获取对方所有的三子连线棋子ID
            const opponentThreePieces = getOpponentThreeLines(opponentPlayer);
            
            pieces.forEach(p => {
                // 对方棋子且不在三子连线中才能被吃
                p.isEatable = p.player === opponentPlayer && !opponentThreePieces.has(p.id);
                p.isSelected = false;
            });
            
            // 检查是否有可吃的棋子
            const hasEatable = pieces.some(p => p.isEatable);
            if (!hasEatable) {
                showTip('对方所有棋子都在三子连线中，无法吃子，自动切换回合');
                setTimeout(() => {
                    pieces.forEach(p => p.isEatable = false);
                    switchPlayer();
                    redraw();
                }, 1500);
            } else {
                showTip('请吃掉对方不在三子连线中的棋子（红色边框标记）');
            }
            
            selectedPiece = null;
            redraw();
        }

        function eatPiece(targetPiece) {
            // 检查目标棋子是否在对方的三子连线中
            if (targetPiece.isInThree) {
                showTip('不能选择吃掉对方三子连线中的棋子');
                return;
            }
            
            // 记录被吃棋子的位置ID，用于落棋阶段的冷却判断
            lastEatenPositionId = targetPiece.point.id;
            eatenPositionCooldown = true; // 仅在落棋阶段有效
            
            const index = pieces.findIndex(p => p.id === targetPiece.id);
            pieces.splice(index, 1);
            
            pieces.forEach(p => {
                p.isEatable = false;
                p.isSelected = false;
                p.isInThree = false;
            });

            // 检查是否胜利（仅在行棋阶段）
            if (checkWin()) {
                showTip(`${currentPlayer === Player.WHITE ? '白棋' : '红棋'}胜利！`);
                setTimeout(initGame, 2000);
                return;
            }

            redraw();
            
            // 落子阶段吃子后由被吃方（对手）重新落子
            if (currentStage === GameStage.PLACING) {
                // 设置当前玩家为被吃方，让其重新落子
                currentPlayer = targetPiece.player;
                showTip(`${currentPlayer === Player.WHITE ? '白棋' : '红棋'}被吃，需重新落子`);
            } else {
                switchPlayer(); // 行棋阶段正常切换玩家
            }
        }

        function movePiece(targetPoint) {
            if (!selectedPiece) return false;
            
            // 检查是否是合法移动位置（相邻位置）
            const validMoves = adjacencyMap[selectedPiece.point.id] || [];
            const isValidMove = validMoves.some(p => isSamePoint(p, targetPoint));
            
            if (!isValidMove) {
                showTip('只能移动到相邻位置');
                return false;
            }

            // 执行移动
            const newPiece = new Piece(targetPoint, selectedPiece.player);
            pieces.push(newPiece);
            
            // 移除原棋子
            const index = pieces.findIndex(p => p.id === selectedPiece.id);
            pieces.splice(index, 1);
            
            // 清除选择状态
            selectedPiece = null;
            pieces.forEach(p => {
                p.isSelected = false;
                p.isInThree = false;
            });
            
            // 检查是否形成三子连线（用于吃子）
            const hasThreeInARow = checkThreeInARow(newPiece);
            if (hasThreeInARow) {
                markEatablePieces(newPiece.player);
            } else {
                // 行棋阶段移动后，无论是否是冷却位置都解除冷却标记
                if (currentStage === GameStage.MOVING) {
                    eatenPositionCooldown = false;
                }
                switchPlayer();
            }
            
            redraw();
            return true;
        }

        function switchPlayer() {
            currentPlayer = currentPlayer === Player.WHITE ? Player.RED : Player.WHITE;
            
            // 切换玩家时，如果是落棋阶段且有冷却位置，则解除冷却
            if (currentStage === GameStage.PLACING && eatenPositionCooldown) {
                eatenPositionCooldown = false;
            }
            
            if (currentStage === GameStage.PLACING) {
                showTip(`${currentPlayer === Player.WHITE ? '白棋' : '红棋'}回合，请落子`);
            } else {
                showTip(`${currentPlayer === Player.WHITE ? '白棋' : '红棋'}回合，可选择自己的棋子移动`);
            }
        }

        function redraw() {
            drawBoard();
            pieces.forEach(piece => piece.draw());
        }

        // 取消选择棋子
        function deselectPiece() {
            if (selectedPiece) {
                selectedPiece.isSelected = false;
                selectedPiece = null;
                redraw();
                
                if (currentStage === GameStage.PLACING) {
                    showTip(`${currentPlayer === Player.WHITE ? '白棋' : '红棋'}回合，请落子`);
                } else {
                    showTip(`${currentPlayer === Player.WHITE ? '白棋' : '红棋'}回合，请选择要移动的棋子`);
                }
            }
        }

        // 选择棋子（支持重新选择）
        function selectPiece(piece) {
            // 检查是否是当前玩家的棋子
            if (piece.player !== currentPlayer) {
                showTip('只能选择自己的棋子');
                return;
            }

            // 如果已经选中了其他棋子，则先取消选中
            if (selectedPiece && selectedPiece.id !== piece.id) {
                selectedPiece.isSelected = false;
            }

            // 设置新的选中状态
            piece.isSelected = !piece.isSelected;
            selectedPiece = piece.isSelected ? piece : null;
            
            redraw();
            
            // 显示提示信息
            if (piece.isSelected) {
                showTip('已选中棋子，可移动到高亮位置或选择其他棋子');
            } else {
                showTip(`${currentPlayer === Player.WHITE ? '白棋' : '红棋'}回合，请选择要移动的棋子`);
            }
        }


        // 事件处理
        function handleCanvasClick(event) {
            const rect = canvas.getBoundingClientRect();
            const clickX = event.clientX - rect.left;
            const clickY = event.clientY - rect.top;

            // 查找点击的点位
            const clickedPoint = points.find(point => {
                const dx = clickX - point.x;
                const dy = clickY - point.y;
                return Math.sqrt(dx * dx + dy * dy) <= pieceRadius * 1.5;
            });

            // 查找点击的棋子
            const clickedPiece = pieces.find(piece => {
                const dx = clickX - piece.x;
                const dy = clickY - piece.y;
                return Math.sqrt(dx * dx + dy * dy) <= pieceRadius;
            });

            // 处理可吃棋子
            if (clickedPiece && clickedPiece.isEatable) {
                eatPiece(clickedPiece);
                return;
            }

            // 落子阶段逻辑
            if (currentStage === GameStage.PLACING) {
                if (clickedPoint) {
                    // 检查是否是刚被吃的位置且处于冷却中（仅落棋阶段有此限制）
                    if (lastEatenPositionId === clickedPoint.id && eatenPositionCooldown) {
                        showTip('该位置刚被吃子，落棋阶段需等一手后才能落棋');
                        return;
                    }
                    
                    // 检查是否已有棋子
                    const hasPiece = pieces.some(p => isSamePoint(p.point, clickedPoint));
                    if (!hasPiece) {
                        const newPiece = new Piece(clickedPoint, currentPlayer);
                        pieces.push(newPiece);
                        
                        // 检查是否形成三子连线（用于吃子判定）
                        const hasThreeInARow = checkThreeInARow(newPiece);
                        if (hasThreeInARow) {
                            markEatablePieces(newPiece.player);
                        } else {
                            // 没有形成三子连线则正常切换玩家
                            switchPlayer();
                        }
                        
                        redraw();
                        
                        // 检查是否进入移动阶段
                        if (pieces.length >= gameConfig.maxPieces && !hasThreeInARow) {
                            currentStage = GameStage.MOVING;
                            showTip('落子阶段结束，开始行棋阶段，可选择自己的棋子移动');
                            // 进入行棋阶段后清除冷却限制
                            eatenPositionCooldown = false;
                        }
                    }
                }
                return;
            }

            // 行棋阶段逻辑（无冷却位置限制）
            if (currentStage === GameStage.MOVING) {
                // 如果点击了自己的棋子，允许重新选择
                if (clickedPiece) {
                    selectPiece(clickedPiece);
                    return;
                }
                
                // 如果有选中的棋子，尝试移动
                if (selectedPiece && clickedPoint) {
                    const hasPiece = pieces.some(p => isSamePoint(p.point, clickedPoint));
                    if (!hasPiece) {
                        movePiece(clickedPoint);
                    }
                }
            }
        }

        // 切换棋盘背景
        function changeBackground(bgType) {
            gameConfig.currentBackground = bgType;
            // 更新选中状态
            document.querySelectorAll('.bg-option').forEach(option => {
                if (option.dataset.bg === bgType) {
                    option.classList.add('selected');
                } else {
                    option.classList.remove('selected');
                }
            });
            redraw();
        }

        function initGame() {
            canvas = document.getElementById("canvas");
            ctx = canvas.getContext("2d");
            calcAdaptiveParams(); // 计算自适应参数，包含DPR处理
            pieces = [];
            currentStage = GameStage.PLACING;
            currentPlayer = Player.WHITE;
            selectedPiece = null;
            lastEatenPositionId = null;
            eatenPositionCooldown = false;
            drawBoard();
            showTip(`${currentPlayer === Player.WHITE ? '白棋' : '红棋'}回合，请落子`);
            
            // 绑定事件
            canvas.removeEventListener('click', handleCanvasClick);
            canvas.addEventListener('click', handleCanvasClick);
            
            document.getElementById('restart').addEventListener('click', initGame);
            document.getElementById('deselect').addEventListener('click', deselectPiece);
            
            // 绑定背景选择事件
            document.querySelectorAll('.bg-option').forEach(option => {
                option.addEventListener('click', () => changeBackground(option.dataset.bg));
            });
        }

        // 窗口大小变化时重新计算布局
        window.addEventListener('resize', () => {
            const oldPieces = [...pieces]; // 保存当前棋子
            // 清除现有画布
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            // 重新计算参数，包括DPR
            calcAdaptiveParams();
            drawBoard();
            
            // 重新创建棋子（适配新尺寸）
            pieces = oldPieces.map(p => {
                // 找到对应新的点位（通过坐标匹配）
                const newPoint = points.find(pt => 
                    Math.abs(pt.x - p.x) < 1 && Math.abs(pt.y - p.y) < 1
                );
                return newPoint ? new Piece(newPoint, p.player) : p;
            });
            
            redraw();
        });

        // 初始化游戏
        initGame();
    </script>
</body>

</html>
